`timescale 1ns / 1ps
////////////////////////////////////////////////
////////////////////////////Ϊ PC����ѡ����ȷ��ֵ
module PC_mux(
    input [31:0] PCin,
    input [31:0] imm,
    input [31:0] rst,
    input br, isx,
    output [31:0] PC_imm,
    output [31:0] PC4,
    output reg [31:0] PCout
);
assign PC_imm = PCin + imm;
assign PC4 = PCin + 4;
always@(*) begin
    if(isx) PCout = rst;                 //jalr pc = x1 + off
    else if(br) PCout = PCin + imm;      //��ת���֧����
    else PCout = PCin + 4; 
end
endmodule
///////////////////////////////////////////////
/////////////////////PCȡַģ��
module PC(
    input clk,
    input rst,
    input [31:0] PCin,
    output reg [31:0] PCout
);
initial begin
    PCout <= 32'h3000;
end
always@(posedge clk) begin
    if(rst) PCout <= 32'h3000;
    else PCout <= PCin;
end
endmodule
///////////////////////////////////////////////
/////////////////////////////��ָ��洢���ж�������
module InsMem(
    input clk,we,
    input [7:0] addr,
    output [31:0] data
);
reg [7:0] imem[255:0];
initial begin
    $readmemb("D:\\sort.txt",imem);
end
assign data={imem[addr+3],imem[addr+2],imem[addr+1],imem[addr]};
endmodule
/////////////////////////////////////////////////
////////////////////////////�Ĵ�����ջ
module RegFile(
    input clk,rst,
    input [4:0] r1,
    input [4:0] r2,
    input we,
    input [4:0] writer,//Ŀ��Ĵ���
    input [31:0] wd,
    output [31:0] rd1,//�����Ƕ���������
    output [31:0] rd2
);
reg [31:0] regF [0:31];//�Ĵ�����
integer i;
assign rd1 = regF[r1];
assign rd2 = regF[r2];
initial for(i=0;i<32;i=i+1) regF[i]<=0;
always@(posedge clk) begin
    if(rst) begin
      for(i = 0; i<32; i= i + 1) begin 
        regF[i] <= 0; 
      end
    end
    else if(we && (writer>0)) regF[writer] <= wd;//д�Ĵ���
end
endmodule
//////////////////////////////////////////////////
/////////////////////////////���Ƶ�Ԫ��ѡ��ָ��
module ControlUnit(
    input [6:0] opcode,
    input [6:0] func6,
    input [2:0] func3,
    output reg jal,
    output reg branch,
    output reg brlt,
    output reg [1:0] regS,
    output reg [3:0] ALUop,
    output reg MemWr,
    output reg ALUsrc,
    output reg RegW,
    output reg PCx1,
    output reg nequal
);
always@(*) begin
    case(opcode) 
       7'b0110011: begin //add ,sub
           if(func6 == 7'b0000000) ALUop = 0;
           if(func6 == 7'b0100000) ALUop = 1;
           jal = 0;
           branch = 0;
           brlt = 0;
           regS = 0;
           MemWr = 0;
           ALUsrc = 0;
           RegW = 1;
           PCx1 = 0;
           nequal=0;
       end
       7'b0010011: begin //addi
           ALUop = 0;
           jal = 0;
           branch = 0;
           brlt = 0;
           regS = 0;
           MemWr = 0;
           ALUsrc = 1;
           RegW = 1;
           PCx1 = 0;
           nequal=0;
       end
       7'b0010011: begin //SLLI���߼�����
           ALUop = 3;
           jal = 0;
           branch = 0;
           brlt = 0;
           regS = 0;
           MemWr = 0;
           ALUsrc = 1;
           RegW = 1;
           PCx1 = 0;
           nequal=0;
       end
       7'b0010111: begin//auipc
           ALUop = 0;
           jal = 0;
           branch = 0;
           brlt = 0;
           regS = 3;
           MemWr = 0;
           ALUsrc = 1;
           RegW = 1; 
           PCx1 = 0;
           nequal=0;
        end
       7'b0000011: begin// LW
            ALUop = 0;//+
            jal = 0;
            branch = 0;
            brlt = 0;
            regS = 1;
            MemWr = 0;
            ALUsrc = 1;
            RegW = 1;
            PCx1 = 0;
            nequal=0;
        end
       7'b0100011: begin// SW
            ALUop = 0;//+
            jal = 0;
            branch = 0;
            brlt = 0;
            regS = 0;
            MemWr = 1;
            ALUsrc = 1;//Imm
            RegW = 0;
            PCx1 = 0;
            nequal=0;
        end
       7'b1100011: begin
           if(func3 == 3'b000) begin// BEQ
               ALUop = 1; //ALU�Ĳ���
               jal = 0;
               branch = 1;
               brlt = 0;
               regS = 2;//PC+4
               MemWr = 0; //�Ƿ�д�ڴ�
               ALUsrc = 0;//ALU��Դ��reg
               RegW = 0;  //�Ƿ�д�Ĵ���
               nequal=0;
            end
            if(func3 == 3'b101) begin// BGE
               ALUop = 1; //ALU�Ĳ���
               jal = 0;
               branch = 1;
               brlt = 1;
               regS = 2;//PC+4
               MemWr = 0; //�Ƿ�д�ڴ�
               ALUsrc = 0;//ALU��Դ��reg
               RegW = 0;  //�Ƿ�д�Ĵ���
               nequal=0;
            end
            if(func3==3'b001) begin//BNEZ
                ALUop = 1;
                jal = 0;
                branch = 0;
                brlt = 1;
                regS = 2;//PC+4
                MemWr = 0; //�Ƿ�д�ڴ�
                ALUsrc = 1;//ALU��Դ��������
                RegW = 0;  //�Ƿ�д�Ĵ���
                nequal=1;
            end
       end
       7'b0010011: begin//ORI
           ALUop = 2; //2���������߼���
           jal = 0;
           branch = 0;
           brlt = 0;
           regS = 0;//д�����ݣ�0��ʾALU,1��ʾMEM��2��ʾPC+4
           MemWr = 0;
           ALUsrc = 1;//Imm
           RegW = 1;
           PCx1 = 0;
           nequal=0;
       end
       7'b1101111: begin//jal
           ALUop = 0;
           jal = 1;
           branch = 0;
           brlt = 0;
           regS = 2;//PC+4
           MemWr = 0;
           ALUsrc = 0;//Imm
           RegW = 1;
           PCx1 = 0;
           nequal=0;
       end
    endcase
end                
endmodule
///////////////////////////////////////////////////
////////////////����Ҫ������ʱ����Щ����ָ���е�������������
module Imm_gen(
    input [31:0] ins,
    output reg [31:0] imm
);
always@(*) begin
    case(ins[6:0])
       7'b0010011: begin
           if(ins[31:25]==7'b0000000) //SLLI
               imm={27'h0,ins[24:20]};
           else if(ins[31] == 1)     //ADDI
               imm = {20'hfffff,ins[31:20]};
           else imm =  {20'h0,ins[31:20]};
       end
       7'b0010111: begin//auipc
           imm = {ins[31:12],12'b0};
       end
       7'b1101111: begin//JAL
           if(ins[31] == 1) 
                imm = {11'h7ff,ins[31],ins[19:12],ins[20],ins[30:21],1'h0};
           else imm = {11'h0,ins[31],ins[19:12],ins[20],ins[30:21],1'h0};
       end     
       7'b0000011: begin//LW
           if(ins[31] == 1)
               imm = {20'hfffff,ins[31:20]};
           else imm =  {20'h0,ins[31:20]};
       end
       7'b0100011: begin//SW
           if(ins[31] == 1)
               imm = {20'hfffff,ins[31:25],ins[11:7]};
           else imm = {20'h0,ins[31:25],ins[11:7]};
       end
       7'b1100011: begin
           if (imm[14:12]==3'b001)   //BNEZ  ��0ʱ��ת
               imm=32'b0;
           else                      //BEQ BGE�� ���ʱ��ת
           begin
               if(ins[31] == 1) imm[31:13] = 19'h7ffff;
               else imm[31:13] = 19'h0;
               imm[12:0] = {ins[31],ins[7],ins[30:25],ins[11:8],1'b0};
           end
       end
       7'b0010011: begin//ORI
           imm=ins[31:20];
       end
    endcase
end      
endmodule
////////////////////////////////////////////////////////////
////////////////////32λALU���ṩ+��-��|,��������
module ALU(
    input [3:0] opc,
    input [31:0] sr1,
    input [31:0] sr2,
    output zero,
    output more,
    output reg [31:0] res
);
assign zero = (res == 0)? 1:0;  //���Ϊ0ʱ��zero=1�����������ת
assign more = ~res[31];         //���Ϊ+ʱ��less=1�����ڴ�����ת
always@(*) begin
    case(opc)
       0: res = sr1 + sr2;
       1: res = sr1 - sr2;
       2: res = sr1 | sr2;
       3: res = sr1 << sr2;
    endcase
end
endmodule
//////////////////////////////////////////////////////////
///////////////////ѡ��ALU�ĵ�2�������������������ǼĴ�����//
module ALU_mux(
    input [31:0] rd2,
    input [31:0] imm,
    input ALUsrc,
    output reg [31:0] data
);
always@(*) begin
    if(ALUsrc) 
        data = imm;
    else 
        data = rd2;
end
endmodule
//////////////////////////////////////////////////////////
/////////////////////////���ݴ洢�����ɶ���д
module DataMem(
    input clk,
    input rst, we,
    input [7:0] addr,
    input [31:0] wdata,            
    output [31:0] rdata           
    );
reg [7:0] dmem[255:0];
initial begin
    $readmemb("D:\\test2.txt", dmem);
end
wire [7:0] addr_=addr[7:0];         //�Ӵ洢���ж�������
assign data={dmem[addr_+3],dmem[addr_+2],dmem[addr_+1],dmem[addr_]};
always@(posedge clk) begin
    if(we) begin                   //������д��洢��
        dmem[addr]<=wdata[7:0];
        dmem[addr+1]<=wdata[15:8];
        dmem[addr+2]<=wdata[23:16];
        dmem[addr+3]<=wdata[31:24];
        end
    end
endmodule
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
module RF_mux(
    input [1:0] regS,//�����ź�
    input [31:0] PC,
    input [31:0] dm,
    input [31:0] res,
    input [31:0] pcimm,
    output reg [31:0] rf_d
);
always@(*) begin
    case(regS) 
       0: rf_d = res;//ALU���
       1: rf_d = dm;//���ݴ洢��
       2: rf_d = PC;//PC+4
       3: rf_d = pcimm;//PC+������
    endcase
end
endmodule
//////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////
module  CPU (
    input clk, 
    input rst,
    input [31:0]PC,
    input [31:0]inst,
    output ALUop,
    output ALUsrc,
    output regS,
    output MemWr,
    output RegW,
    output PCx1
);
//PC
wire [31:0] PC, ia, PCout, inst;
//�����ź�
wire jal;
wire branch;
wire brlt;
wire nequal;
wire [1:0] regS;
wire [3:0] ALUop;
wire MemWr;
wire ALUsrc;
wire RegW;
wire PCx1;
//insmem
wire [31:0] ins_data;
wire ins_w;
assign ins_w = 0;
//Regfile
wire [31:0] rd1, rd2, wd;
//immgen
wire[31:0] imm_num;
//alu mux
wire[31:0] data_2;
//alu
wire zero;
wire more;
wire [31:0] alu_rst;
//data mem
wire dm_we;
assign dm_we = ~alu_rst[10] & MemWr;
assign PCout=PC;
wire [31:0] m_rd1, m_rd2;
wire[31:0] dm_out;
//RF mux
wire [31:0] rf_wd;
//PC mux
wire br;
wire [31:0] pcimm, PC4;
assign br = (branch & zero) | jal | (brlt & more) | ((~zero) & nequal);
//��ģ������
PC pc1(clk, rst, PC, PCout);
InsMem insm(clk, ins_w, PCout[9:2], inst);
ControlUnit con(inst[6:0], inst[31:25], inst[14:12], jal, branch, brlt, regS, ALUop, MemWr, ALUsrc, RegW, PCx1, nequal);
RegFile rf(clk, rst, inst[19:15], inst[24:20], RegW, inst[11:7], rf_wd, rd1, rd2);
Imm_gen img(inst, imm_num);
ALU_mux am(rd2, imm_num, ALUsrc, data_2);
ALU alu(ALUop, rd1, data_2, zero, more, alu_rst);
//��Ҫд��32λ�ĵ�ַ
DataMem dm(clk, rst, dm_we, alu_rst, rd2, m_rd1);
RF_mux rf_m(regS, PC4, dm_out, alu_rst, pcimm, rf_wd);
PC_mux pcm(PCout, imm_num, alu_rst, br, PCx1, pcimm, PC4, PC);
endmodule